;  This file is part of tornado.
; 
;    tornado is free software: you can redistribute it And/Or modify
;    it under the terms of the GNU General Public License As published by
;    the Free Software Foundation, either version 3 of the License, Or
;    any later version.
; 
;    tornado is distributed in the hope that it will be useful,
;    but WITHOUT ANY WARRANTY; without even the implied warranty of
;    MERCHANTABILITY Or FITNESS For A PARTICULAR PURPOSE.  See the
;    GNU General Public License For more details.
; 
;    You should have received a copy of the GNU General Public License
;    along With tornado.  If Not, see <http://www.gnu.org/licenses/>. 2
; 

XIncludeFile "PacketManager.pbi"

; /////////////////////////////////////////////////////////////////////////////
;
; /////////////////////////////////////////////////////////////////////////////
Global PacketNumber.u = 0


; /////////////////////////////////////////////////////////////////////////////
;
; /////////////////////////////////////////////////////////////////////////////
Procedure.i createPacket(type.a = #PACKET_CUSTOM)
  *p.sPacket = new(sPacket)
  If *p
    *p\mData    = AllocateMemory(#PACKET_LIMIT)
    *p\mIsValid = #True 
    PokeA(*p\mData,type)
    PokeU(*p\mData+1,PacketNumber)
    PacketNumber + 2
    PacketNumber % 65535
    *p\mOffset = 2
    ProcedureReturn *p
  EndIf
  ProcedureReturn #Null 
EndProcedure

; /////////////////////////////////////////////////////////////////////////////
;
; /////////////////////////////////////////////////////////////////////////////
ProcedureDLL.i createPacket2()
  *p.sPacket = new(sPacket)
  If *p
    *p\mData    = AllocateMemory(#PACKET_LIMIT)
    *p\mIsValid = #True 
    value.a = #PACKET_CUSTOM
    PokeA(*p\mData,value)
    PokeU(*p\mData+1,PacketNumber)
    PacketNumber + 2
    PacketNumber % 65535
    *p\mOffset = 2
    ProcedureReturn *p
  EndIf
  ProcedureReturn #Null 
EndProcedure

; /////////////////////////////////////////////////////////////////////////////
;
; /////////////////////////////////////////////////////////////////////////////
ProcedureDLL freePacket(*p.sPacket)
  FreeMemory(*p\mData)
  FreeMemory(*p)
EndProcedure


; /////////////////////////////////////////////////////////////////////////////
;
; /////////////////////////////////////////////////////////////////////////////
ProcedureDLL.i getPacketFromData(*memory)
  *p.sPacket = new(sPacket)
  If *p
    If (PeekA(*memory) = #PACKET_SYSTEM) Or (PeekA(*memory) = #PACKET_CUSTOM)
      *p\mData = AllocateMemory(#PACKET_LIMIT)
      CopyMemory(*memory,*p\mData,#PACKET_LIMIT)
      *p\mOffset = 2
      ProcedureReturn *p
    Else
      ProcedureReturn #Null     
    EndIf
  EndIf
  ProcedureReturn #Null 
EndProcedure

; /////////////////////////////////////////////////////////////////////////////
;
; /////////////////////////////////////////////////////////////////////////////
ProcedureDLL.u getPacketNumber(*p.sPacket)
  If *p
    ProcedureReturn PeekW(*p\mData+1)
  EndIf 
  ProcedureReturn #False 
EndProcedure

; /////////////////////////////////////////////////////////////////////////////
;
; /////////////////////////////////////////////////////////////////////////////
Procedure.l getPacketSize(*p.sPacket)
  If *p
    ProcedureReturn *p\mOffset 
  EndIf
EndProcedure
  
; /////////////////////////////////////////////////////////////////////////////
;
; /////////////////////////////////////////////////////////////////////////////
; ProcedureDLL.l getPacketNumber(*p.sPacket)
;   If *p
;     ProcedureReturn PeekA(*p\mData+1)
;   EndIf
; EndProcedure

; /////////////////////////////////////////////////////////////////////////////
;
; /////////////////////////////////////////////////////////////////////////////
Procedure.a getPacketType(*p.sPacket)
  If *p
    ProcedureReturn PeekA(*p\mData)
  EndIf
EndProcedure

; /////////////////////////////////////////////////////////////////////////////
;
; /////////////////////////////////////////////////////////////////////////////
ProcedureDLL pushPacket(*p.sPacket, *data, type.a, size.l = 0)
  stringFormat.l = #PB_Ascii
  If *p
    Select type
      Case #TYPE_ASCII
        PokeA(*p\mData + *p\mOffset, type)          : *p\mOffset + 1
        PokeA(*p\mData + *p\mOffset, PeekA(*data))  : *p\mOffset + 1
        
      Case #TYPE_BYTE 
        PokeA(*p\mData + *p\mOffset, type)          : *p\mOffset + 1
        PokeB(*p\mData + *p\mOffset, PeekB(*data))  : *p\mOffset + 1
        
      Case #TYPE_CHAR
        PokeA(*p\mData + *p\mOffset, type)          : *p\mOffset + 1
        
        CompilerIf #PB_Compiler_Unicode = 1
          PokeC(*p\mData + *p\mOffset, PeekC(*data))  : *p\mOffset + 2
        CompilerElse
          PokeC(*p\mData + *p\mOffset, PeekC(*data))  : *p\mOffset + 1
        CompilerEndIf
        
      Case #TYPE_WORD 
        PokeA(*p\mData + *p\mOffset, type)          : *p\mOffset + 1
        PokeW(*p\mData + *p\mOffset, PeekW(*data))  : *p\mOffset + 2
        
      Case #TYPE_UNICODE
        PokeA(*p\mData + *p\mOffset, type)          : *p\mOffset + 1
        PokeU(*p\mData + *p\mOffset, PeekU(*data))  : *p\mOffset + 2
        
      Case #TYPE_INTEGER
        PokeA(*p\mData + *p\mOffset, type)          : *p\mOffset + 1
        
        CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
          PokeI(*p\mData + *p\mOffset, PeekI(*data))  : *p\mOffset + 4
        CompilerEndIf
        
        CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
          PokeI(*p\mData + *p\mOffset, PeekI(*data))  : *p\mOffset + 8
        CompilerEndIf
        
      Case #TYPE_FLOAT
        PokeA(*p\mData + *p\mOffset, type)          : *p\mOffset + 1
        PokeF(*p\mData + *p\mOffset, PeekF(*data))  : *p\mOffset + 4
        
      Case #TYPE_DOUBLE
        PokeA(*p\mData + *p\mOffset, type)          : *p\mOffset + 1
        PokeD(*p\mData + *p\mOffset, PeekD(*data))  : *p\mOffset + 8
        
      Case #TYPE_QUAD
        PokeA(*p\mData + *p\mOffset, type)          : *p\mOffset + 1
        PokeQ(*p\mData + *p\mOffset, PeekQ(*data))  : *p\mOffset + 8
        
      Case #TYPE_LONG
        PokeA(*p\mData + *p\mOffset, type)          : *p\mOffset + 1
        PokeL(*p\mData + *p\mOffset, PeekL(*data))  : *p\mOffset + 4
        
      Case #TYPE_STRING ; Check for last character ( bug ? )
        PokeA(*p\mData + *p\mOffset, type)              : *p\mOffset + 1
        If *data
          PokeL(*p\mData + *p\mOffset, Len(PeekS(*data))) : *p\mOffset + 4 ; IAM
          PokeS(*p\mData + *p\mOffset, PeekS(*data,Len(PeekS(*data)),stringFormat),Len(PeekS(*data)),stringFormat)      : *p\mOffset + Len(PeekS(*data))
        Else
          PokeL(*p\mData + *p\mOffset, 0) : *p\mOffset + 4 
          PokeS(*p\mData + *p\mOffset, "",stringFormat)      : *p\mOffset + 0
        EndIf
      Case #TYPE_STRUCT
        PokeA(*p\mData + *p\mOffset, type)           : *p\mOffset + 1
        PokeL(*p\mData + *p\mOffset, size)           : *p\mOffset + 4
        CopyMemory(*data,*p\mData+ *p\mOffset, size) : *p\mOffset + size
        
        
    EndSelect
    
    If *p\mOffset < #PACKET_LIMIT
      *p\mIsValid = #True
      ProcedureReturn #False 
    Else 
      *p\mIsValid = #False 
      ProcedureReturn #True 
    EndIf 
  Else
    ProcedureReturn #False
  EndIf 
EndProcedure

; /////////////////////////////////////////////////////////////////////////////
;
; /////////////////////////////////////////////////////////////////////////////
ProcedureDLL popPacket(*p.sPacket, *data)
   stringFormat.l = #PB_Ascii
  If *p
    
    type.a = PeekA(*p\mData + *p\mOffset) : *p\mOffset + 1
    
    Select type
      Case #TYPE_ASCII
        PokeA(*data, PeekA(*p\mData + *p\mOffset)) : *p\mOffset + 1
      Case #TYPE_BYTE 
        PokeB(*data, PeekB(*p\mData + *p\mOffset)) : *p\mOffset + 1
      Case #TYPE_CHAR 
        CompilerIf #PB_Compiler_Unicode = 1
          PokeC(*data, PeekC(*p\mData + *p\mOffset)) : *p\mOffset + 2
        CompilerElse
          PokeC(*data, PeekC(*p\mData + *p\mOffset)) : *p\mOffset + 1
        CompilerEndIf
      Case #TYPE_WORD 
        PokeW(*data, PeekW(*p\mData + *p\mOffset)) : *p\mOffset + 2
      Case #TYPE_UNICODE  
        PokeU(*data, PeekU(*p\mData + *p\mOffset)) : *p\mOffset + 2
      Case #TYPE_INTEGER
        CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
          PokeI(*data, PeekI(*p\mData + *p\mOffset)) : *p\mOffset + 4
        CompilerEndIf
        
        CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
          PokeI(*data, PeekI(*p\mData + *p\mOffset)) : *p\mOffset + 8
        CompilerEndIf
      Case #TYPE_FLOAT
        PokeF(*data, PeekF(*p\mData + *p\mOffset)) : *p\mOffset + 4
      Case #TYPE_DOUBLE
        PokeD(*data, PeekD(*p\mData + *p\mOffset)) : *p\mOffset + 8
      Case #TYPE_QUAD
        PokeQ(*data, PeekQ(*p\mData + *p\mOffset)) : *p\mOffset + 8
      Case #TYPE_LONG 
        PokeL(*data, PeekL(*p\mData + *p\mOffset)) : *p\mOffset + 4
      Case #TYPE_STRING 
        size.l = PeekL(*p\mData + *p\mOffset)           : *p\mOffset + 4
        PokeS(*data,PeekS(*p\mData + *p\mOffset,size,stringFormat),size,stringFormat)  : *p\mOffset + size
      Case #TYPE_STRUCT
        size.l = PeekL(*p\mData + *p\mOffset) : *p\mOffset + 4
        CopyMemory(*p\mData + *p\mOffset, *data, size)     : *p\mOffset + size
        
    EndSelect
  EndIf
EndProcedure

; /////////////////////////////////////////////////////////////////////////////
;
; /////////////////////////////////////////////////////////////////////////////
Procedure.i receivePacket(connectionID.i)

  *buffer = AllocateMemory(#PACKET_LIMIT)
  ReceiveNetworkData(connectionID,*buffer,#PACKET_LIMIT)
  *packet.sPacket = getPacketFromData(*buffer)
  
  If *packet <> #Null
    FreeMemory(*buffer)
    ProcedureReturn *packet
  Else
    FreeMemory(*buffer)
    ProcedureReturn #Null
  EndIf 

  
EndProcedure

; /////////////////////////////////////////////////////////////////////////////
;
; /////////////////////////////////////////////////////////////////////////////
ProcedureDLL.b serverPacketQueued(*s.sTornadoServer)
  If *s
    If ListSize(*s\mCustomPacketReceived()) > 0
      ProcedureReturn #True 
    Else
      ProcedureReturn #False
    EndIf 
  EndIf 
  ProcedureReturn #False 
EndProcedure

; /////////////////////////////////////////////////////////////////////////////
;
; /////////////////////////////////////////////////////////////////////////////
ProcedureDLL.i serverGetReceivedPacket(*s.sTornadoServer)
  If *s
    FirstElement(*s\mCustomPacketReceived())
    *packet.sPacket = getPacketFromData( *s\mCustomPacketReceived()\mData )
    FreeMemory(*s\mCustomPacketReceived()\mData)
    DeleteElement(*s\mCustomPacketReceived())
    ProcedureReturn *packet
  EndIf 
EndProcedure

; /////////////////////////////////////////////////////////////////////////////
;
; /////////////////////////////////////////////////////////////////////////////
ProcedureDLL.b clientPacketQueued(*c.sTornadoClient)
  If *c
    If ListSize(*c\mCustomPacketReceived()) > 0
      ProcedureReturn #True 
    Else
      ProcedureReturn #False
    EndIf 
  EndIf 
  ProcedureReturn #False 
EndProcedure

; /////////////////////////////////////////////////////////////////////////////
;
; /////////////////////////////////////////////////////////////////////////////
ProcedureDLL.i clientGetReceivedPacket(*c.sTornadoClient)
  If *c
    FirstElement(*c\mCustomPacketReceived())
    *packet.sPacket = getPacketFromData( *c\mCustomPacketReceived()\mData )
    FreeMemory(*c\mCustomPacketReceived()\mData)
    DeleteElement(*c\mCustomPacketReceived())
    ProcedureReturn *packet
  EndIf 
EndProcedure

; /////////////////////////////////////////////////////////////////////////////
;
; /////////////////////////////////////////////////////////////////////////////
ProcedureDLL.l getPacketFromSlot(*p.sPacket)
  If *p
    ProcedureReturn PeekL(*p\mData + #PACKET_LIMIT - 4)
  EndIf 
EndProcedure  


; /////////////////////////////////////////////////////////////////////////////
;
; /////////////////////////////////////////////////////////////////////////////
